import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;
import java.util.*;
/**
 * La clase Keyboard crea un teclado virtal
 */
public class Keyboard extends JPanel
{
    /**
     * Construye un nuevo objeto Keyboard que representa un teclado
     * virtual
     * @throws AWTException si la configuración de la plataforma no 
     * permite el control de entradas de bajo nivel
     */
    public Keyboard() throws AWTException
    {
        this.setFocusable(false);
        keyTask = new Robot();
        createControlButtons();
        createShiftButtons();
        altKeypress = false;
        altGrKeypress = false;
        this.setLayout(new GridBagLayout());        
        Border etchedBorder = BorderFactory.createEtchedBorder();
        this.setBorder(etchedBorder);
        GridBagConstraints constraints = new GridBagConstraints( 0,
                                              0,
                                              2,
                                              1,
                                              1,
                                              0.2,
                                              GridBagConstraints.CENTER,
                                              GridBagConstraints.BOTH,
                                              new Insets(0, 0, 0, 0),
                                              0,
                                              0
                                            );
        String[][] keyID = {
                            {"VK_BACK_QUOTE","VK_1","VK_2","VK_3","VK_4","VK_5","VK_6","VK_7","VK_8","VK_9","VK_0","VK_MINUS","VK_EQUALS","VK_BACK_SPACE"},
                            {"VK_TAB","VK_Q","VK_W","VK_E","VK_R","VK_T","VK_Y","VK_U","VK_I","VK_O","VK_P","VK_OPEN_BRACKET","VK_CLOSE_BRACKET","VK_BACK_SLASH"},
                            {"VK_ENTER"},
                            {"VK_CAPS_LOCK","VK_A","VK_S","VK_D","VK_F","VK_G","VK_H","VK_J","VK_K","VK_L","VK_SEMICOLON","VK_QUOTE"},
                            {"VK_SHIFT","VK_Z","VK_X","VK_C","VK_V","VK_B","VK_N","VK_M","VK_COMMA","VK_PERIOD","VK_SLASH","VK_SHIFT"},
                            {"VK_CONTROL","VK_ALT","VK_SPACE","VK_ALT_GRAPH","VK_CONTROL"},
                            {"VK_HOME","VK_UP","VK_END","VK_PAGE_UP"},
                            {"VK_LEFT","VK_DOWN","VK_RIGHT","VK_PAGE_DOWN"}
                           };
        this.add(createRow(keyID[0]), constraints);
        constraints.gridy = 1;
        constraints.gridwidth = 1;
        constraints.weightx = 0.9;
        this.add(createRow(keyID[1]), constraints);
        this.add(createRow(keyID[2]),new GridBagConstraints( 1,
                                              1,
                                              1,
                                              2,
                                              0.1,
                                              0.4,
                                              GridBagConstraints.CENTER,
                                              GridBagConstraints.BOTH,
                                              new Insets(0, 0, 0, 0),
                                              0,
                                              0
                                            ));
        constraints.gridy = 2;
        this.add(createRow(keyID[3]), constraints);
        constraints.gridy = 3;
        constraints.gridwidth = 2;
        constraints.weightx = 1;
        this.add(createRow(keyID[4]), constraints);
        constraints.gridy = 4;
        this.add(createLastRow(keyID[5],keyID[6],keyID[7]), constraints);
    }
    /**
     * Genera una fila de teclado
     * @param keyID un arreglo de nombres de teclas en clave que se desea
     * agregar a la fila
     * @return una lámina que contiene una fila de teclado
     */
    private JPanel createRow(String[] keyID)
    {
        JPanel row = new JPanel(new GridBagLayout());
        int i = 0;
        GridBagConstraints constraints = new GridBagConstraints( i,
                                              0,
                                              1,
                                              1,
                                              1,
                                              1,
                                              GridBagConstraints.CENTER,
                                              GridBagConstraints.BOTH,
                                              new Insets(0, 0, 0, 0),
                                              0,
                                              0
                                            );
        for(String keyID_ : keyID)
        {
            row.add(getKey(keyID_), constraints);
            i++;
            constraints.gridx = i;
        }    
        return row;
    }
    /**
     * Genera la ultima fila que contiene las teclas control, alt, space, entre otras
     * @param keyID_1 un arreglo de nombres de teclas en clave que se desea
     * agregar a la fila
     * @param keyID_2 un arreglo de nombres de teclas en clave que se desea
     * agregar a la fila
     * @param keyID_3 un arreglo de nombres de teclas en clave que se desea
     * agregar a la fila
     * @return una lámina que contiene una fila de teclado
     */
    private JPanel createLastRow(String[] keyID_1,String[] keyID_2,String[] keyID_3)
    {
        JPanel row = new JPanel(new GridBagLayout());
        JPanel panel = new JPanel(new GridLayout(2,0));
        panel.add(createRow(keyID_2));
        panel.add(createRow(keyID_3));
        GridBagConstraints constraints = new GridBagConstraints( 0,
                                              0,
                                              1,
                                              1,
                                              0.06,
                                              1,
                                              GridBagConstraints.CENTER,
                                              GridBagConstraints.BOTH,
                                              new Insets(0, 0, 0, 0),
                                              0,
                                              0
                                            );
        row.add(getKey(keyID_1[0]),constraints);
        constraints.gridx = 1;
        row.add(getKey(keyID_1[1]),constraints);
        constraints.gridx = 2;
        constraints.weightx = 0.48;
        row.add(getKey(keyID_1[2]),constraints);
        constraints.gridx = 3;
        constraints.weightx = 0.06;
        row.add(getKey(keyID_1[3]),constraints);
        constraints.gridx = 4;
        row.add(getKey(keyID_1[4]),constraints);
        constraints.gridx = 5;
        constraints.weightx = 0.18;
        row.add(panel,constraints);
        return row;
    }
    /**
     * Genera una tecla
     * @param vkID nombre en clave que hace referencia a una tecla
     * @return un bóton que representa una tecla
     */
    private JButton getKey(final String vkID)
    {
        JButton key = new JButton(NewComp.getIcon(vkID));
        key.setFocusable(false);
        int i;
        if(vkID.equals("VK_CONTROL"))
        {
            if(firstControl)
                i = 0;
            else
                i = 1;
            firstControl = false;
            control[i].addActionListener(new
                ActionListener()
                {
                    public void actionPerformed(ActionEvent evento)
                    {
                        Integer intId = (Integer) idValue.handleGetObject(vkID);
                        if(controlKeypress)
                        {
                            keyTask.keyRelease(intId.intValue());
                            controlKeypress = false;
                            control[0].setIcon(NewComp.getIcon(vkID));
                            control[1].setIcon(NewComp.getIcon(vkID));
                        }else
                        {
                            keyTask.keyPress(intId.intValue());
                            controlKeypress = true;
                            control[0].setIcon(NewComp.getIcon(vkID+"_PRESS"));
                            control[1].setIcon(NewComp.getIcon(vkID+"_PRESS"));
                        }
                    }
                });
            return control[i];
        }
        if(vkID.equals("VK_SHIFT"))
        {
            if(firstShift)
                i = 0;
            else
                i = 1;
            firstShift = false;
            shift[i].addActionListener(new
                ActionListener()
                {
                    public void actionPerformed(ActionEvent evento)
                    {
                        Integer intId = (Integer) idValue.handleGetObject(vkID);
                        if(shiftKeypress)
                        {
                            keyTask.keyRelease(intId.intValue());
                            shiftKeypress = false;
                            shift[0].setIcon(NewComp.getIcon(vkID));
                            shift[1].setIcon(NewComp.getIcon(vkID));
                        }else
                        {
                            keyTask.keyPress(intId.intValue());
                            shiftKeypress = true;
                            shift[0].setIcon(NewComp.getIcon(vkID+"_PRESS"));
                            shift[1].setIcon(NewComp.getIcon(vkID+"_PRESS"));
                        }
                    }
                });
            return shift[i];
        }
        if(vkID.equals("VK_ALT"))
        {
            JButton alt = new JButton(NewComp.getIcon(vkID));
            alt.setFocusable(false);
            alt.addActionListener(new
                ActionListener()
                {
                    public void actionPerformed(ActionEvent evento)
                    {
                        Integer intId = (Integer) idValue.handleGetObject(vkID);
                        JButton button = (JButton) evento.getSource();
                        if(altKeypress)
                        {
                            //keyTask.keyRelease(intId.intValue());
                            altKeypress = false;
                            button.setIcon(NewComp.getIcon(vkID));
                        }else
                        {
                            //keyTask.keyPress(intId.intValue());
                            altKeypress = true;
                            button.setIcon(NewComp.getIcon(vkID+"_PRESS"));
                        }
                    }
                });
            return alt;
        }
        if(vkID.equals("VK_ALT_GRAPH"))
        {
            JButton altGr = new JButton(NewComp.getIcon(vkID));
            altGr.setFocusable(false);
            altGr.addActionListener(new
                ActionListener()
                {
                    public void actionPerformed(ActionEvent evento)
                    {
                        Integer intId = (Integer) idValue.handleGetObject(vkID);
                        JButton button = (JButton) evento.getSource();
                        if(altGrKeypress)
                        {
                            altGrKeypress = false;
                            button.setIcon(NewComp.getIcon(vkID));
                        }else
                        {
                            altGrKeypress = true;
                            button.setIcon(NewComp.getIcon(vkID+"_PRESS"));
                        }
                    }
                });
            return altGr;
        }
        key.addActionListener(new
                ActionListener()
                {
                    public void actionPerformed(ActionEvent evento)
                    {
                        Integer intId = (Integer) idValue.handleGetObject(vkID);
                        keyTask.keyPress(intId.intValue());
                        keyTask.keyRelease(intId.intValue());
                        InfoManager.addCommand(KbdCmdId.CmdId.CHARACTER,intId);
                    }
                });
        return key;
    }
    /**
     * Genera las teclas llamadas Control
     */
    private void createControlButtons()
    {
        String vkID = "VK_CONTROL";
        firstControl = true;
        control = new JButton[2];
        controlKeypress = false;
        control[0] = new JButton(NewComp.getIcon(vkID));
        control[0].setFocusable(false);
        control[1] = new JButton(NewComp.getIcon(vkID));
        control[1].setFocusable(false);
    }
    /**
     * Genera las teclas llamadas shift
     */
    private void createShiftButtons()
    {
        String vkID = "VK_SHIFT";
        firstShift = true;
        shift = new JButton[2];
        shiftKeypress = false;
        shift[0] = new JButton(NewComp.getIcon(vkID));
        shift[0].setFocusable(false);
        shift[1] = new JButton(NewComp.getIcon(vkID));
        shift[1].setFocusable(false);
    }
    /**
     * Lista donde se relacionan los nombres en clave de las teclas 
     * con su respectivo valor de tecla virtual
     */
    private static class KeyIdValue extends ListResourceBundle
    {
        public KeyIdValue(){}
        public Object[][] getContents(){return contents;  }
        private static final Object[][] contents =
        {
            {"VK_CLEAR",new Integer(KeyEvent.VK_CLEAR),new Integer(KeyEvent.VK_CLEAR)},
            {"VK_1",new Integer(KeyEvent.VK_1)},
            {"VK_2",new Integer(KeyEvent.VK_2)},
            {"VK_3",new Integer(KeyEvent.VK_3)},
            {"VK_4",new Integer(KeyEvent.VK_4)},
            {"VK_5",new Integer(KeyEvent.VK_5)},
            {"VK_6",new Integer(KeyEvent.VK_6)},
            {"VK_7",new Integer(KeyEvent.VK_7)},
            {"VK_8",new Integer(KeyEvent.VK_8)},
            {"VK_9",new Integer(KeyEvent.VK_9)},
            {"VK_0",new Integer(KeyEvent.VK_0)},
            {"VK_QUOTE",new Integer(KeyEvent.VK_QUOTE)},
            {"VK_DEAD_TILDE",new Integer(KeyEvent.VK_DEAD_TILDE)},
            {"VK_BACK_SPACE",new Integer(KeyEvent.VK_BACK_SPACE)},
            {"VK_TAB",new Integer(KeyEvent.VK_TAB)},
            {"VK_Q",new Integer(KeyEvent.VK_Q)},
            {"VK_W",new Integer(KeyEvent.VK_W)},
            {"VK_E",new Integer(KeyEvent.VK_E)},
            {"VK_R",new Integer(KeyEvent.VK_R)},
            {"VK_T",new Integer(KeyEvent.VK_T)},
            {"VK_Y",new Integer(KeyEvent.VK_Y)},
            {"VK_U",new Integer(KeyEvent.VK_U)},
            {"VK_I",new Integer(KeyEvent.VK_I)},
            {"VK_O",new Integer(KeyEvent.VK_O)},
            {"VK_P",new Integer(KeyEvent.VK_P)},
            {"VK_DEAD_GRAVE",new Integer(KeyEvent.VK_DEAD_GRAVE)},
            {"VK_PLUS",new Integer(KeyEvent.VK_PLUS)},
            {"VK_ENTER",new Integer(KeyEvent.VK_ENTER)},
            {"VK_CAPS_LOCK",new Integer(KeyEvent.VK_CAPS_LOCK)},
            {"VK_A",new Integer(KeyEvent.VK_A)},
            {"VK_S",new Integer(KeyEvent.VK_S)},
            {"VK_D",new Integer(KeyEvent.VK_D)},
            {"VK_F",new Integer(KeyEvent.VK_F)},
            {"VK_G",new Integer(KeyEvent.VK_G)},
            {"VK_H",new Integer(KeyEvent.VK_H)},
            {"VK_J",new Integer(KeyEvent.VK_J)},
            {"VK_K",new Integer(KeyEvent.VK_K)},
            {"VK_L",new Integer(KeyEvent.VK_L)},
            {"VK_DEAD_DOUBLEACUTE",new Integer(KeyEvent.VK_DEAD_DOUBLEACUTE)},
            {"VK_DEAD_DIAERESIS",new Integer(KeyEvent.VK_DEAD_DIAERESIS)},
            {"VK_DEAD_BREVE",new Integer(KeyEvent.VK_DEAD_BREVE)},
            {"VK_SHIFT",new Integer(KeyEvent.VK_SHIFT)},
            {"VK_LESS",new Integer(KeyEvent.VK_LESS)},
            {"VK_Z",new Integer(KeyEvent.VK_Z)},
            {"VK_X",new Integer(KeyEvent.VK_X)},
            {"VK_C",new Integer(KeyEvent.VK_C)},
            {"VK_V",new Integer(KeyEvent.VK_V)},
            {"VK_B",new Integer(KeyEvent.VK_B)},
            {"VK_N",new Integer(KeyEvent.VK_N)},
            {"VK_M",new Integer(KeyEvent.VK_M)},
            {"VK_COMMA",new Integer(KeyEvent.VK_COMMA)},
            {"VK_PERIOD",new Integer(KeyEvent.VK_PERIOD)},
            {"VK_DEAD_ABOVEDOT",new Integer(KeyEvent.VK_DEAD_ABOVEDOT)},
            {"VK_CONTROL",new Integer(KeyEvent.VK_CONTROL)},
            {"VK_ALT",new Integer(KeyEvent.VK_ALT)},
            {"VK_SPACE",new Integer(KeyEvent.VK_SPACE)},
            {"VK_ALT_GRAPH",new Integer(KeyEvent.VK_ALT_GRAPH)},
            {"VK_HOME",new Integer(KeyEvent.VK_HOME)},
            {"VK_UP",new Integer(KeyEvent.VK_UP)},
            {"VK_END",new Integer(KeyEvent.VK_END)},
            {"VK_LEFT",new Integer(KeyEvent.VK_LEFT)},
            {"VK_DOWN",new Integer(KeyEvent.VK_DOWN)},
            {"VK_RIGHT",new Integer(KeyEvent.VK_RIGHT)},
            {"VK_PAGE_UP",new Integer(KeyEvent.VK_PAGE_UP)},
            {"VK_PAGE_DOWN",new Integer(KeyEvent.VK_PAGE_DOWN)},
            {"VK_BACK_QUOTE",new Integer(KeyEvent.VK_BACK_QUOTE)},
            {"VK_BACK_SLASH",new Integer(KeyEvent.VK_BACK_SLASH)},
            {"VK_CLOSE_BRACKET",new Integer(KeyEvent.VK_CLOSE_BRACKET)},
            {"VK_OPEN_BRACKET",new Integer(KeyEvent.VK_OPEN_BRACKET)},
            {"VK_EQUALS",new Integer(KeyEvent.VK_EQUALS)},
            {"VK_SEMICOLON",new Integer(KeyEvent.VK_SEMICOLON)},
            {"VK_SLASH",new Integer(KeyEvent.VK_SLASH)},
            {"VK_MINUS",new Integer(KeyEvent.VK_MINUS)}
        };
    }
    static
    {
        idValue = new KeyIdValue();
    }
    private boolean firstControl;
    private boolean firstShift;
    private JButton[] control;
    //private JButton alt;
    //private JButton altGr;
    private JButton[] shift;
    private boolean shiftKeypress;
    private boolean altKeypress;
    private boolean altGrKeypress;
    private boolean controlKeypress;
    private Robot keyTask;
    private static KeyIdValue idValue;
}